#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/11/14 20:03
# @Author  : name
# @File    : PcaOption.py

import numpy as np
import cv2

ImageNumber = 54
SameImageTrainTimes = 4;

class PcaOption(object):

    ImageHeight = 100
    ImageWidth = 100
    ImageGrayNumber = 10000
    ValueNumber = 100  # 特征数量
    RightDistance = 5000   # 识别正确的最小距离

    def __init__(self,ImageNumber):
        '''
        初始化函数
        :param ImageNumber: 训练集图片数量
        '''
        self.ImageNumber = ImageNumber
        self.GrayImage = []

    def ReadGrayImage(self,FilePath):
        '''
        读取训练集灰度图片
        :param FilePath: 训练集文件目录
        :return:
        '''
        for i in range(1,ImageNumber + 1):
            for j in range(0,SameImageTrainTimes):
                OneImgPath = FilePath + "/" + str(i) + "/" + str(i) + "_" + str(j) + ".bmp"
                OneImage = cv2.imread(OneImgPath,0)
                OneImage = np.resize(OneImage,(self.ImageGrayNumber,))
                self.GrayImage.append(OneImage)

    def CalculateCenterData(self):
        '''
        每列减去均值，中心化
        :return:
        '''
        CenterData = np.empty((self.ImageNumber,self.ImageGrayNumber))
        MeanValue = np.mean(self.GrayImage,axis=0)
        for i in range(self.ImageNumber):
            OneCenterData = []
            for j in range(self.ImageGrayNumber):
                data = self.GrayImage[i][j] - MeanValue[j]
                if data < 0:
                    OneCenterData.append(0)
                else:
                    OneCenterData.append(data)
            CenterData[i] = OneCenterData
        return CenterData

    def CalculateCovMatrix(self,CenterData):
        '''
        计算协方差矩阵
        :param DeCenterData: 去中心化后的数据
        :return:返回协方差矩阵
        '''
        CovMatrix = np.cov(CenterData,rowvar = False)
        return CovMatrix

    def CalculateFeature(self,CovMatrix):
        '''
        计算特征值和特征向量，前 K 个特征值 和 向量存入文件
        :return:前 K个 特征向量
        '''
        FileWrite1 = open("./ImageData/FeatureVector.txt","a")
        FileWrite2 = open("./ImageData/FeatureValue.txt", "a")
        FeatureValue,FeatureVector = np.linalg.eig(CovMatrix)
        FeatureValueIndex = np.argsort(FeatureValue)
        MyFeatureValueIndex = FeatureValueIndex[:-self.ValueNumber-1:-1]
        MyFeatureVector = FeatureVector[:,MyFeatureValueIndex]
        # 写入前10个特征值
        j = 0
        for i in reversed(np.sort(FeatureValue)):
            FileWrite2.write(str(i))
            FileWrite2.write("\n")
            j = j + 1
            if j == 10:
                break
        #写入特征向量
        for i in range(self.ImageGrayNumber):
            for j in range(self.ValueNumber):
                FileWrite1.write(str(MyFeatureVector[i][j]))
                if j != self.ValueNumber - 1:
                    FileWrite1.write(",")
            FileWrite1.write("\n")
        FileWrite1.close()
        FileWrite2.close()
        return MyFeatureVector

    def CalculateDropMatrix(self,GrayImage,MyFeatureVector):
        '''
        将训练集降维
        :param GrayImage:  灰度图像数据
        :param MyFeatureVector: 前 K 个特征向量
        :return:  降维后数据
        '''
        DropMatrix = np.dot(GrayImage,MyFeatureVector)
        return DropMatrix

    def MakeTrainingTxt(self,Filepath):
        '''
        对训练集处理，保存降维矩阵，提高识别效率
        :param Filepath: 训练集目录
        :return:
        '''
        FileWrite = open("./ImageData/DropMatrix.txt", "a")
        self.ReadGrayImage(Filepath)
        CenterData = self.CalculateCenterData()
        CovMatrix = self.CalculateCovMatrix(CenterData)
        MyFeatureVector = self.CalculateFeature(CovMatrix)
        DropMatrix = self.CalculateDropMatrix(self.GrayImage,MyFeatureVector)
        for i in range(self.ImageNumber):
            for j in range(self.ValueNumber):
                FileWrite.write(str(DropMatrix[i][j]))
                if j != self.ValueNumber -1:
                    FileWrite.write(",")
            FileWrite.write("\n")
        FileWrite.close()


    def PcaRecognition(self,TestGrayImage):
        '''
        PCA 人脸识别
        :param TestImagePath: 测试图片路径
        :return:
        '''
        flag = 0
        TrainDropMatrix = np.genfromtxt('/home/wyl/QtProject/build-Face_PCA-ROS_QT-Debug/ImageData/DropMatrix.txt', dtype=complex, delimiter=',')
        TestGrayImage = np.resize(TestGrayImage,(self.ImageGrayNumber,))
        MyFeatureVector = np.genfromtxt('/home/wyl/QtProject/build-Face_PCA-ROS_QT-Debug/ImageData/FeatureVector.txt', dtype=complex, delimiter=',')
        TestDropMatrix = self.CalculateDropMatrix(TestGrayImage,MyFeatureVector)

        AllDistanceList = []
        EuclidDistance = []
        for i in range(0,self.ImageNumber,4):
            DistanceList = []
            for j in range(i,i+4):
                Distance = np.linalg.norm(TrainDropMatrix[j] - TestDropMatrix)
                DistanceList.append(Distance)
            AllDistanceList.append(DistanceList)
        for OneList in AllDistanceList:
            distance = 0
            for one in OneList:
                distance += one
            EuclidDistance.append(distance/4)
        correct = EuclidDistance.index(min(EuclidDistance))
        SerialNumber = AllDistanceList[correct].index((min(AllDistanceList[correct])))
        if AllDistanceList[correct][SerialNumber] < self.RightDistance:
            flag = int(str(correct + 1) + str(SerialNumber))
            #print("检测到人脸")
        else:
            flag = -1
            #print("未检测到人脸")
        return flag




    def PersonsRecognition(self,TestImagePath):
        Image = cv2.imread(TestImagePath,0)
        Image = np.split(Image, 2 ,axis = 1)
        flag1 = self.PcaRecognition(Image[0])
        flag2 = self.PcaRecognition(Image[1])
        return flag1,flag2

    def RecognitionRightRate(self,ImageNum):
        '''
        统计识别正确率
        :param ImageNum: 测试图片数量
        :return: 识别正确率
        '''
        FilePath = "/home/wyl/QtProject/build-Face_PCA-ROS_QT-Debug/ImageData/TestImage"
        List = []
        sum = 0
        for i in range(1, ImageNum+1):
            OneImgPath = FilePath + "/" + str(i) + "/" + str(i) + "_" + str(4) + ".bmp"
            Img = cv2.imread(OneImgPath,0)
            correct = self.PcaRecognition(Img)
            correct = int(correct/10)
            if correct == i:
                List.append(correct)
                sum += 1
        rate = int(sum / ImageNum * 10000)
        return rate

    def RecognitionRate(self, ImageNum):
        '''
        统计识别率
        :param ImageNum: 测试图片数量
        :return: 识别率
        '''
        FilePath = "/home/wyl/QtProject/build-Face_PCA-ROS_QT-Debug/ImageData/TestImage2/"
        sum = 0
        for i in range(ImageNum):
            OneImgPath = FilePath + "/" + str(i) + ".bmp"
            Img = cv2.imread(OneImgPath, 0)
            correct = self.PcaRecognition(Img)
            if (correct == -1 and i < 10) or (correct != -1 and i >=10):
                sum = sum + 1
        rate = int(sum / ImageNum * 10000)
        return rate

def PcaProcess(ImagePath):
    Image = cv2.imread(ImagePath,0)
    PcaTrain = PcaOption(ImageNumber * SameImageTrainTimes)
    flag =  PcaTrain.PcaRecognition(Image)
    return flag

def PersonsRecognitionProcess(ImagePath)
    Image = cv2.imread(ImagePath,0)
    PcaTrain = PcaOption(ImageNumber * SameImageTrainTimes)
    flag_left,flag_right = PcaTrain.PersonsRecognition
    return flag_left,flag_right

def showRate(test_number)
    PcaTrain = PcaOption(ImageNumber * SameImageTrainTimes)
    return PcaTrain.RecognitionRate(test_number)

def showRightRate(test_number)
    PcaTrain = PcaOption(ImageNumber * SameImageTrainTimes)
    return PcaTrain.RecognitionRightRate(test_number)

#if __name__ == '__main__':
#    flag = PcaProcess("/home/wyl/QtProject/build-Face_PCA-ROS_QT-Debug/ImageData/TestImage/1/1_4.bmp")
#    print(flag)
    #PcaTrain = PcaOption(216)                              # 初始化
    #PcaTrain.MakeTrainingTxt("./ImageData/TrainImage")  # 训练
    #rate = PcaTrain.RecognitionRightRate(54)              # 统计识别准确率
    #rate = PcaTrain.RecognitionRate(20)                   # 统计识别率
    # flag = PcaTrain.PcaRecognition("imgpath")            # 单张识别
    # flag = PcaTrain.PersonsRecognition("./ImageData/TestImage2/1.bmp")  # 多张识别
